#!/usr/bin/evn python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   文件名称 :     middleware
   文件功能描述 :   功能描述
   创建人 :       小钟同学
   创建时间 :          2021/12/8
-------------------------------------------------
   修改描述-2021/12/8:         
-------------------------------------------------
"""
from jaeger_client import Config
from grpc_interceptor import ServerInterceptor
from grpc_interceptor.exceptions import GrpcException
from grpc_interceptor.exceptions import NotFound
from typing import Any, Callable
from opentracing import tags
from opentracing.propagation import Format
import opentracing
import logging
import grpc
import re
from opentracing.ext import tags as ot_tags
from grpcframe.middlewares.tracer.utils import grpc_opentracing_init, _add_peer_tags
from grpcframe.zyxgrpc.globals import set_active_tracer_span

class Zyxopentracing(ServerInterceptor):

    def __init__(self, log_payloads=False):
        pass
        self.tracer_interceptor = grpc_opentracing_init()
        # 是否 记录我们的request请求提交的payloads信息，请求参数信息
        self._log_payloads = log_payloads

    def get_tracer_interceptor(self):
        return self.tracer_interceptor

    @staticmethod
    def _before_request_start_span(request, servicer_context, method_name, _tracer):
        """
        Gather various info about the request and start new span with the data.
        """

        span_context = None
        error = None
        # 获取到对应的客户端提交过来的请求头信息
        metadata = servicer_context.invocation_metadata()
        # 根据请求头信息，进行对应的解析透传过来的spanContxt（反序列化）。
        # 以下参考开源项目的处理方式
        try:
            if metadata:
                # HTTP_HEADERS是 http的透传传输的方式
                # span_context = self._tracer.extract(
                #     opentracing.Format.HTTP_HEADERS, dict(metadata))
                # TEXT_MAP 处理的是grapc的方式
                # 开始解析透传过来的spanContxt（反序列化
                span_context = _tracer.extract(opentracing.Format.TEXT_MAP, dict(metadata))
                # print("反编译过来！！！",span_context里面包含有相关的trace_id和span_id等信息，可以打印dir(span_context))
        except (opentracing.UnsupportedFormatException, opentracing.InvalidCarrierException, opentracing.SpanContextCorruptedException) as e:
            error = e
            logging.exception('tracer.extract() failed')
        # 开始处理扩展纤细tags的添加
        # metadatametadatametadata(('uber-trace-id', '3fec1eb736f1bff7:2a263288419df8a1:0:1'), ) value = '3fec1eb736f1bff7:2a263288419df8a1:0:1'
        peer_tags = {
            tags.COMPONENT: 'grpc',
            # 注意这个标记是服务端的
            tags.SPAN_KIND: tags.SPAN_KIND_RPC_SERVER
        }
        # 处理我们peer_tags,
        _add_peer_tags(servicer_context.peer(), peer_tags)
        # 创建对应的span
        span = _tracer.start_span(operation_name=method_name, child_of=span_context, tags=peer_tags)
        # 如果链路调用过程存在错误信息，则也需要记录到我们的时间里面，并记录错误信息
        if error is not None:
            span.set_tag('error', True)
            span.log_kv({'event': 'error', 'error.object': error})

        return span

    def intercept(self, method: Callable, request: Any, servicer_context: grpc.ServicerContext, method_name: str) -> Any:
        rsep = None
        # 使用上下文管理的方式处理我们的span
        with self._before_request_start_span(request, servicer_context, method_name, self.tracer_interceptor) as span:
            try:
                if self._log_payloads:
                    span.log_kv({'request': request})
                # 做一层转接的代理处理---存到上下文信息中
                # g.active_tracer_span.set(span)
                set_active_tracer_span(span)
                rsep = method(request, servicer_context)
            except GrpcException as e:
                # 记录错误的信息
                span.set_tag('error', True)
                span.log_kv({'event': 'error', 'error.object': e})
                # 抛出错误信息
                servicer_context.set_code(e.status_code)
                servicer_context.set_details(e.details)
                raise
            finally:
                # 记录非grpc.StatusCode.OK的错误的响应的日志信息到追踪链路上
                # servicer_context 这里做了一层封装道理才可以获取到code，默认的是grpc.StatusCode.OK
                # 如果底层系统已经设置了异常，则直接返回即可
                # print("！！！！！！！rseprsep",dir(servicer_context))
                # print("！！！！！！！rseprsep", dir(servicer_context._state))
                if servicer_context._state.code != grpc.StatusCode.OK and servicer_context._state.details:
                    span.set_tag('error', True)
                    error_log = {'event': 'error', 'error.kind': str(servicer_context.code)}
                    if servicer_context.details is not None:
                        error_log['message'] = servicer_context.details
                    span.log_kv(error_log)
                return rsep
